﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

using King.Extensions;
using King.Infoset.Schema;
using King.Infoset.Reflection;
using King.Reflection;

namespace King.Infoset.Entity {

    public static class Extensions {

        #region Definitions
        private sealed class XsEntityContract {

            #region Private Data Members
            private XsContract m_contract;
            private XsMember m_idMember;
            private XsMember m_refMember;
            private XsMember m_versionMember;
            private MethodBase m_clearDefaults;
            private XsOperation m_refresh;
            #endregion

            #region Constructor
            internal XsEntityContract() {
            }
            internal XsEntityContract(XsContract contract) {
                m_contract = contract;
                m_idMember = contract.Members().SingleOrDefault(o => o.IsEntityId());
                m_refMember = contract.Members().SingleOrDefault(o => o.IsEntityRefId());
                m_versionMember = contract.Members().SingleOrDefault(o => o.IsDefined<VersionAttribute>());
                m_clearDefaults = contract.SerializedType.Get<MethodBase>("ClearDefaults").SingleOrDefault();
                m_refresh = contract.Operations("Refresh").SingleOrDefault(o => !o.IsStatic);
            }
            #endregion

            #region Internal Members
            internal XsContract Contract { get { return m_contract; } }
            internal XsMember GetVersionMember() { return m_versionMember; }
            internal XsMember GetIdMember() { return m_idMember ?? m_refMember; }
            internal bool IsEntity { get { return m_idMember != null; } }
            internal bool IsEntityRef { get { return m_refMember != null; } }
            internal MethodBase ClearDefaults { get { return m_clearDefaults; } }
            internal XsOperation Refresh { get { return m_refresh; } }
            #endregion
        }
        private sealed class XsEntityMember {
            
            #region Private Data Members
            #endregion

            #region Constructor
            internal XsEntityMember() {
            }
            internal XsEntityMember(XsMember member) {
            }
            #endregion

            #region Internal Members
            #endregion
        }
        #endregion

        #region Extensions
        public static bool IsDisallowedOn(this XsMember member, OperationType operationType) {
            var disallowedOn = member.GetAttributes<DisallowedOnAttribute>()
                .Aggregate(default(OperationType), (a, o) => a | o.OperationType);

            return (disallowedOn & operationType) != 0;
        }
        public static bool IsRequiredOn(this XsMember member, OperationType operationType) {
            var requiredOn = member.GetAttributes<RequiredOnAttribute>()
                .Aggregate(default(OperationType), (a, o) => a | o.OperationType);

            var result = (requiredOn & operationType) == operationType;
            return result;
        }
        public static bool IsHideDefault(this XsMember member) {
            var defaultOnCreate = member.GetAttribute<DefaultOnCreateAttribute>();
            var hideDefault = defaultOnCreate != null && defaultOnCreate.HideDefault;
            return hideDefault;
        }
        public static object SerializedDefaultValue(this XsMember member) {
            var defaultOnCreate = member.GetAttribute<DefaultOnCreateAttribute>();
            if (defaultOnCreate == null)
                return null;

            var defaultValue = defaultOnCreate.DefaultValue;
            if (defaultValue == null)
                return null;

            // no defaults for choices
            if (member.IsChoice)
                throw new ArgumentException();

            return defaultValue;
        }
        public static object DeserializedDefaultValue(this XsMember member) {
            var defaultValue = member.SerializedDefaultValue();
            if (defaultValue == null)
                return null;

            var result = member.Contract.Deserialize(defaultValue);
            return result;
        }
        public static bool IsEntityId(this XsMember member) {
            return member.IsDefined<EntityIdAttribute>();
        }
        public static bool IsEntityRefId(this XsMember member) {
            return member.IsDefined<EntityRefAttribute>();
        }

        public static bool IsEntity(this XsContract contract) {
            var result = GetContract(contract);
            return result.IsEntity;
        }
        public static bool IsEntityRef(this XsContract contract) {
            var result = GetContract(contract);
            return result.IsEntityRef;
        }

        public static XsContract GetDeRefContract(this XsContract contract) {
            if (!contract.IsEntityRef())
                throw new ArgumentException();
            var localName = contract.FullName.LocalName;
            var deRefName = contract.Namespace + (localName.Substring(0, localName.Length - "Ref".Length));
            return XsLoader.GetContract(deRefName);
        }
        public static XsContract GetRefContract(this XsContract contract) {
            if (!contract.IsEntity())
                throw new ArgumentException();
            var refName = contract.Namespace + (contract.FullName.LocalName + "Ref");
            return XsLoader.GetContract(refName);
        }
        public static XsMember GetEntityMember(this XsContract contract) {
            var result = GetContract(contract);
            return result.GetIdMember();
        }
        public static long? GetIdOrNull(this XsContract contract, object entity) {
            return (long?)contract.GetEntityMember().GetValue(entity);
        }
        public static long GetId(this XsContract contract, object entity) {
            return (long)contract.GetIdOrNull(entity);
        }
        public static void SetId(this XsContract contract, object entity, long? id) {
            contract.GetEntityMember().SetValue(entity, id);
        }

        public static XsMember GetVersionMember(this XsContract contract) {
            var result = GetContract(contract);
            return result.GetVersionMember();
        }
        public static long GetVersion(this XsContract contract, object entity) {
            var version = contract.GetVersionMember().GetValue(entity);
            if (version == null)
                throw new ArgumentException("Entity must have a version inorder to be updated.");
            return (long)version;
        }
        public static void SetVersion(this XsContract contract, object entity, long? version) {
            contract.GetVersionMember().SetValue(entity, version);
        }

        public static bool IsAdd(this XsOperation operation) {
            return operation.IsDefined<AddAttribute>();
        }
        public static bool IsRemove(this XsOperation operation) {
            return operation.IsDefined<RemoveAttribute>();
        }
        public static bool IsCreate(this XsOperation operation) {
            return operation.IsDefined<CreateAttribute>();
        }
        public static bool IsUpdate(this XsOperation operation) {
            return operation.IsDefined<UpdateAttribute>();
        }
        public static bool IsRefresh(this XsOperation operation) {
            return operation.IsDefined<RefreshAttribute>();
        }
        public static bool IsWrite(this XsOperation operation) {
            var isWrite = operation.IsDefined<WriteAttribute>() ||
                operation.IsDefined<RemoveAttribute>() ||
                operation.IsDefined<SetAttribute>() ||
                operation.IsDefined<AddAttribute>() ||
                operation.IsDefined<CreateAttribute>() ||
                operation.IsDefined<UpdateAttribute>();

            return isWrite;
        }

        public static bool IsRequiredOnCreate(this XsMember member) {
            return member.IsDefined<RequiredOnCreateAttribute>();
        }
        public static bool IsRequiredOnUpdate(this XsMember member) {
            return member.IsDefined<RequiredOnUpdateAttribute>();
        }

        public static void ClearDefaults(this XsContract contract, object target) {
            var clearDefaults = GetContract(contract).ClearDefaults;
            if (clearDefaults != null)
                clearDefaults.Invoke(target, new object[] { });
        }
        public static XsOperation GetRefreshOperation(this XsContract contract) {
            return GetContract(contract).Refresh;
        }
        public static void Refresh(this XsContract contract, object target) {
            var operation = contract.GetRefreshOperation();
            if (operation == null)
                throw new InvalidOperationException();
            operation.MethodInfo.Invoke(target, new object[] { });
        }
        #endregion

        #region Static Private Members
        private static XsEntityContract GetContract(XsContract contract) {
            if (contract == null)
                return new XsEntityContract();

            var entityContract = s_entityContracts.GetValueOrDefault(contract);
            if (entityContract == null)
                s_entityContracts[contract] = entityContract = new XsEntityContract(contract);
            return entityContract;
        }
        private static XsEntityMember GetMember(XsMember member) {
            if (member == null)
                return new XsEntityMember();

            var entityMember = s_entityMembers.GetValueOrDefault(member);
            if (entityMember == null)
                s_entityMembers[member] = entityMember = new XsEntityMember(member);
            return entityMember;
        }
        #endregion

        #region Constructor
        static Extensions() {
            s_entityContracts = new Dictionary<XsContract, XsEntityContract>();
            s_entityMembers = new Dictionary<XsMember, XsEntityMember>();
        }
        #endregion

        #region Static Private Data Members
        private static Dictionary<XsContract, XsEntityContract> s_entityContracts;
        private static Dictionary<XsMember, XsEntityMember> s_entityMembers;
        #endregion
    }

    public struct VisitorNode {

        #region Static Public Members
        public static object Transform(object source, Func<VisitorNode, object> copy) {
            
            return Transform(
                source, 
                o => true, 
                o => true, 
                copy);
        }
        public static object Copy(object source) {

            return Transform(
                source,
                o => true,              // traverseChildren
                o => true,              // shouldCopy
                o => o.ShallowCopyValue());
        }
        public static object Copy(object root, Func<VisitorNode, bool> shouldCopy) {
            return Transform(
                root,
                o => true,              // traverseChildren
                shouldCopy,
                o => o.ShallowCopyValue());
        }
        public static object Copy(
            object root,
            Func<VisitorNode, bool> traverseChildren,
            Func<VisitorNode, bool> shouldCopy) {

            return Transform(root, traverseChildren, shouldCopy, o => o.ShallowCopyValue());
        }
        public static object Transform(
            object root,
            Func<VisitorNode, bool> traverseChildren,
            Func<VisitorNode, bool> shouldCopy,
            Func<VisitorNode, object> copy) {

            if (root is TypedObject)
                root =((TypedObject)root).Object;

            if (root == null)
                return null;

            var shouldCopyInternal =
                (Func<VisitorNode, bool, bool>)((o, hasChildren) =>
                    hasChildren ||                      // always copy internal nodes
                    o.DeserializedValue == root ||      // always copy the root
                    shouldCopy(o));                     // always copy if we we pass the user's filter

            var traverse = TreeTraverse.DepthFirst(     // depth first traverse
                new VisitorNode(root),                  // the root
                o => o.PhysicalChildren,                // do a physical traverse (traverse structs as nodes)
                traverseChildren,                       // optimize traverse via this short circit
                shouldCopyInternal,                     // apply user filter for all leaf nodes
               (o, c) => o.ShallowCopy(copy),           // childSelector; shallow copy the VisitorNode for each child
                o => o.Value).ToArray();                // parentSelector; project just the value

            // a depth first traverse will return leaf nodes first. This is important because it means
            // structs are initialized *before* they are copied into their parent
            foreach (var siblings in traverse) {

                // all siblings are grouped under their parent 
                var parent = siblings.Key;

                // null parent has a single child which is the root. if we find it then we're done
                if (parent == null)
                    return siblings.First().Value;

                // add the children to the parent
                foreach (var child in siblings)
                    child.SetValue(parent, child.Value);
            }

            // unreachable
            throw new ArgumentException();
        }
        public static IEnumerable<T> Traverse<T>(object target) where T : class {
            return Traverse(target, o => o as T, o => (T)o.Value);
        }
        public static IEnumerable<TResult> Traverse<TResult, T>(
            object target,
            Func<object, T> tryCast,
            Func<VisitorNode, TResult> resultSelector) {

            var contract = XsLoader.GetContractOrNull(typeof(T));
            if (contract == null)
                throw new ArgumentException();

            return Traverse(
                target,
                o => true,
               (o, c) =>
                   // only return locations that could contain the target type
                   (o.LocationContract == null || contract.IsReplaceableWith(o.LocationContract)) &&
                   // and actually do contain the target type
                   tryCast(o.Value) != null,
               (o, c) => o)
               .Select(o => resultSelector(o));
        }
        public static IEnumerable<VisitorNode> Traverse(
            object target,
            Func<VisitorNode, bool> shouldReturn) {

            return Traverse(
                target,
                o => true,
                (o, c) => shouldReturn(o),
                (o, c) => o);
        }
        public static IEnumerable<TResult> Traverse<TResult>(
            object target,
            Func<VisitorNode, bool> traverseChildren,
            Func<VisitorNode, bool, bool> shouldReturn,
            Func<VisitorNode, bool, TResult> resultSelector) {

            return TreeTraverse.DepthFirst(
                new VisitorNode(target),                        // traverse the target
                o => o.Children,                                // do a logical traverse; don't return structs as nodes
                traverseChildren,                               // traverseChildren
                shouldReturn,                                   // and actually do contain the target type
                resultSelector,                                 // project the result
                o => o).Select(o => o.Key);
        }
        public static object LeftCopy(
            object left,
            object right,
            Func<VisitorNode, VisitorNode, bool> traverseChildren,
            Func<VisitorNode, VisitorNode, bool> shouldCopy,
            Func<VisitorNode, VisitorNode, VisitorNode> copySelector) {

            return LeftCopy(left, right, null, traverseChildren, shouldCopy, copySelector);
        }
        public static object LeftCopy(
            object left,
            object right,
            object target,
            Func<VisitorNode, VisitorNode, bool> traverseChildren,
            Func<VisitorNode, VisitorNode, bool> shouldCopy,
            Func<VisitorNode, VisitorNode, VisitorNode> copySelector) {

            return LeftTransform(left, right, target, traverseChildren, shouldCopy, copySelector,
                o => o.ShallowCopyValue());
        }
        public static object LeftTransform(
            object left,
            object right,
            object target,
            Func<VisitorNode, VisitorNode, bool> traverseChildren,
            Func<VisitorNode, VisitorNode, bool> shouldCopy,
            Func<VisitorNode, VisitorNode, VisitorNode> copySelector,
            Func<VisitorNode, object> copyValue) {

            if (left == null)
                return null;

            var leftNode = new VisitorNode(left);
            var rightNode = new VisitorNode(right);
            var targetNode = new VisitorNode(target);

            //if (right != null && rightNode.Contract != leftNode.Contract)
            //    if (!rightNode.Contract.IsEnumeration || !leftNode.Contract.IsEnumeration)
            //        throw new ArgumentException();

            //if (target != null && targetNode.Contract != leftNode.Contract)
            //    if (!targetNode.Contract.IsEnumeration || !leftNode.Contract.IsEnumeration)
            //        throw new ArgumentException();

            var shouldCopyInternal = (Func<VisitorNode, VisitorNode, VisitorNode, bool, bool>)
                delegate(VisitorNode l, VisitorNode r, VisitorNode t, bool hasChildren) {
                    // always copy internal node if there are children to be copied
                    if (hasChildren)
                        return true;

                    if (l.ValueCanHaveChildren)
                        return false;

                    return shouldCopy(l, r);
                };

            var childSelector = (Func<VisitorNode, VisitorNode, VisitorNode, bool, VisitorNode>)
                delegate(VisitorNode l, VisitorNode r, VisitorNode t, bool hasChildren) {
                    // childSelector; if we have an existing internal node then re-use it
                    if (hasChildren && t.HasValue && l.Value.GetType() == r.Value.GetType())
                        return t;

                    // else copy the leaf value or create a childless node
                    return copySelector(l, r).ShallowCopy(copyValue);
                };

            var traverse = TreeTraverse.LeftTraverse(
                leftNode,
                rightNode,
                targetNode,
                o => o.PhysicalChildren.ToArray(),      // getChildren; return structs as nodes
                o => o.HasValue,                        // hasValue; predicate to see if left node has a value
                l => default(VisitorNode),              // rightDefaultSelector
                l => default(VisitorNode),              // targetDefaultSelector
                traverseChildren,
                shouldCopyInternal,
                childSelector,      
                o => o.Value)                           // parentSelector
                .ToArray();

            foreach (var siblings in traverse) {

                // all siblings are grouped under their parent 
                var parent = siblings.Key;

                // null parent has a single child which is the root. if we find it then we're done
                if (parent == null) {
                    if (siblings.None())
                        throw new ArgumentException();
                    return siblings.First().Value;
                }

                // add the children to the parent
                foreach (var copy in siblings)
                    copy.SetValue(parent, copy.Value);
            }

            return target;
        }
        public static IEnumerable<TResult> LeftDiff<TResult, T>(
            object left,
            object right,
            Func<VisitorNode, VisitorNode, bool> traverseChildren,
            Func<object, T> tryCast,
            Func<T, T, bool> wherePredicate,
            Func<T, T, TResult> resultSelector) {

            // find all objects of type T which are different

            var contract = XsLoader.GetContractOrNull(typeof(T));
            if (contract == null)
                throw new ArgumentException();

            return VisitorNode.LeftWhere(
                left, right,
                traverseChildren,                                   // traverseChildren
                (l, r, c) => l.IsAssignableFrom(contract) &&        // don't diff if can't possibly be an S
                             l.Equals(r) ?
                             false : tryCast(l.Value) != null &&    // only return values that are of type S
                             wherePredicate((T)l.Value, (T)r.Value),// and satisfy custom where predicate
                (l, r) => resultSelector(
                          tryCast(l.Value), tryCast(r.Value)));     // cast the result values to type of S and project
        }
        public static IEnumerable<T> LeftWhere<T>(
            object left,
            object right,
            Func<VisitorNode, VisitorNode, bool> traverseChildren,
            Func<VisitorNode, VisitorNode, bool, bool> wherePredicate,
            Func<VisitorNode, VisitorNode, T> resultSelector) {

            if (left == null)
                throw new ArgumentNullException();

            var leftNode = new VisitorNode(left);
            var rightNode = new VisitorNode(right);

            if (right != null && rightNode.Contract != leftNode.Contract)
                throw new ArgumentException();

            return TreeTraverse.LeftWhere(
                leftNode,
                rightNode,
                o => o.Children,                            // logical traverse; don't return structs as nodes
                o => o.HasValue,                            // test to see if the VisitorNode has a value
                o => default(VisitorNode),                  // default selector if right is null and left is not
                traverseChildren,
                wherePredicate,
                resultSelector).ToArray();
        }
        #endregion

        #region Static Private Members
        private static bool DiffMerge(VisitorNode targetNode, VisitorNode sourceNode, bool diff) {

            var merge = !diff;
            var target = targetNode.Value;
            var source = sourceNode.Value;

            if (targetNode.Member != sourceNode.Member)
                throw new ArgumentException();

            if (target == null)
                return source == null;

            if (source == null) {

                if (merge)
                    return false;

                throw new ArgumentException();
            }

            if (targetNode.Contract != sourceNode.Contract)
                throw new NotImplementedException();

            // simple 
            if (targetNode.ValueIsSimpleContract)
                return (target != null && target.Equals(source)) || (target == source);

            // enumeration
            if (targetNode.ValueIsEnumeration) {

                var targetList = (IList)target;
                var sourceList = (IEnumerable)source;

                // for now, we expect that any lists encountered are empty and need to 
                // be populated with the elements in the source list
                if (targetList.Count != 0)
                    throw new NotImplementedException();

                // populate the target list
                foreach (var element in sourceList)
                    targetList.Add(element);

                return targetList.Count > 0;
            }

            // xml
            if (targetNode.ValueIsXml)
                return XElement.DeepEquals((XNode)targetNode.Value, (XNode)sourceNode.Value);

            // complex
            KingDebug.Assert(targetNode.Contract.IsComplex);

            // zip up the children
            var leftZip = sourceNode.PhysicalChildren
                .LeftZip(targetNode.PhysicalChildren,
                    (s, t) => new {
                        SourceChildNode = s,
                        TargetChildNode = t
                    });

            var deepEquals = true;
            foreach (var o in leftZip) {

                var targetChildNode = o.TargetChildNode;
                var sourceChildNode = o.SourceChildNode;

                // compare the contractMembers
                bool equals = DiffMerge(targetChildNode, sourceChildNode, diff);

                if (!equals)
                    deepEquals = false;

                if (!equals) {

                    if (sourceChildNode.Value == null)
                        // must be a merge; merges never clear values; only update 'em
                        KingDebug.Assert(merge);

                    else if (targetChildNode.Value == null || targetChildNode.ValueIsSimpleContract)
                        // target is different than the source so update it
                        targetChildNode.SetValue(target, sourceChildNode.Value);

                } else if (diff && sourceChildNode.Value != null) {
                    // same and diff was set so clear the target
                    targetChildNode.SetValue(target, null);
                }
            }

            return deepEquals;
        }
        #endregion

        #region Private Data Members
        private object m_deserializedValue;
        private object m_value;
        private int m_index;
        private object m_parent;
        private XsContract m_contract;
        private XsMember m_member;
        private bool m_isTypedObject;
        #endregion

        #region Constructor
        public VisitorNode(object value)
            : this(value, null, null) {
        }
        internal VisitorNode(object value, object parent, int index)
            : this(value, null, parent, index) {
        }
        internal VisitorNode(object value, XsMember contractMember, object parent)
            : this(value, contractMember, parent, 0) {
        }
        private VisitorNode(object value, XsMember contractMember, object parent, int index) {
            m_parent = parent;
            m_index = index;
            m_member = contractMember;
            m_contract = null;
            m_isTypedObject = false;

            var typedObject = value as TypedObject;
            if (typedObject != null) {
                value = typedObject.Object;
                m_contract = typedObject.Contract;
                m_isTypedObject = true;
            }

            if (m_contract == null && contractMember != null)
                m_contract = contractMember.GetContractOrNull(value);

            if (m_contract == null && value != null)
                m_contract = XsLoader.GetContractOrNull(value);

            m_value = m_deserializedValue = value;
            if (m_contract != null)
                m_value = m_contract.Serialize(m_value);
        }
        #endregion

        #region Private Members
        private IEnumerable<VisitorNode> ChildrenWhere(Func<XsMember, bool> predicate) {

            if (m_value == null ||                            // empty values can't have children
                m_contract == null ||               // can only be null for SimpleContracts...
                m_contract.IsSimple ||  // ...which never have any children
                m_contract.IsXml) {                 // xml Contracts don't have any children 
                yield break;

                // Enumeration
            } else if (m_contract.IsEnumeration) {
                var index = 0;
                foreach (var element in (IEnumerable)m_value)
                    yield return new VisitorNode(element, m_value, index++);

                // ComplexContract
            } else {
                foreach (var contractMember in m_contract.Members().Where(o => predicate(o)))
                    yield return new VisitorNode(contractMember.GetValue(Value), contractMember, Value);
            }
        }
        private object PyhsicalParent { get { return m_parent; } }
        private XsContract LocationContractOrThrow {
            get {
                if (m_member == null)
                    throw new InvalidOperationException();
                return m_member.Contract;
            }
        }
        #endregion

        #region Public Members (Contract)
        public XsContract Contract { get { return m_contract; } }
        public bool ValueIsString { get { return m_contract != null && m_contract.IsString; } }
        public bool ValueIsEnumeration { get { return m_contract != null && m_contract.IsEnumeration; } }
        public bool ValueIsXml { get { return m_contract == null || m_contract.IsXml; } }
        public bool ValueIsGroupContract { get { return m_contract != null && m_contract.IsGroup; } }
        public bool ValueIsComplexContract { get { return m_contract != null && m_contract.IsComplex; } }
        public bool ValueIsSimpleContract { get { return m_contract == null || m_contract.IsSimple; } }
        public bool ValueIsEnum { get { return m_contract == null || m_contract.IsList; } }
        public bool ValueIsEmpty {
            get {
                if (Value == null)
                    return true;

                if (Value is string && string.Empty == (string)Value)
                    return true;

                if (Value is IEnumerable && ((IEnumerable)Value).Cast<object>().None())
                    return true;

                return false;
            }
        }
        public bool ValueCanHaveChildren { get { return m_contract != null && ValueIsComplexContract || ValueIsEnumeration; } }
        #endregion

        #region Public Members (ContractMember)
        public XsMember Member { get { return m_member; } }
        public XsContract LocationContract { get { return m_member == null ? null : m_member.Contract; } }
        public XsContract DeclaringContract {
            get {
                if (m_member == null)
                    return null;

                return m_member.DeclaringContract;
            }
        }
        public bool LocationIsObject { get { return m_member == null || LocationContractOrThrow.IsObject; } }
        public bool LocationIsString { get { return m_member == null ? false : LocationContractOrThrow.IsString; } }
        public bool LocationIsEnumeration { get { return m_member == null ? false : LocationContractOrThrow.IsEnumeration; } }
        public bool LocationIsEnumContract { get { return m_member == null ? false : LocationContractOrThrow.IsList; } }
        public bool LocationIsGroupContract { get { return m_member == null ? false : LocationContractOrThrow.IsGroup; } }
        public bool LocationIsComplexContract { get { return m_member == null ? false : LocationContractOrThrow.IsComplex; } }
        public bool LocationIsSimpleContract { get { return m_member == null ? false : LocationContractOrThrow.IsSimple; } }
        public bool IsAssignableFrom(XsContract contract) {
            if (LocationIsObject)
                return true;
            return LocationContract.IsReplaceableWith(contract);
        }
        #endregion

        #region Public Members (Parent, Children, Value)
        // parent
        public object Parent { get { return m_parent; } }

        // children (logical)
        public IEnumerable<VisitorNode> Children {
            get {
                foreach (var child in ChildrenWhere(o => true)) {
                    if (child.Contract != null && child.Contract.IsGroup) {
                        foreach (var groupMember in child.Children)
                            yield return groupMember;
                    } else {
                        yield return child;
                    }
                }
            }
        }
        public IEnumerable<VisitorNode> PhysicalChildren {
            get {
                return ChildrenWhere(o => true).ToArray();
            }
        }

        // value
        public int Index {
            get { return m_index; }
        }
        public bool HasValue {
            get { return m_value != null; }
        }
        public object DeserializedValue { get { return m_deserializedValue; } }
        public object Value {
            get {
                // TODO: this code hacked in before ship
                //if (Loader.GetContract(m_value) == null && m_contract != null)
                //    return new TypedObject(m_value, m_contract);
                return m_value;
            }
        }
        public IEnumerable ValueAsEnumeration { get { return m_value as IEnumerable; } }
        public XElement ValueAsXElement { get { return m_value as XElement; } }
        public string ValueAsString { get { return m_value as string; } }
        #endregion

        #region Public Members (Latebound, Copy)
        public object Activate() {

            if (ValueIsEnumeration)
                return m_value.GetType().GetEnumerationElementType().MakeList().Activate();

            var contract = m_contract;
            if (m_contract == null)
                contract = m_member.Contract;

            var result = contract.SerializedType.Activate();

            contract.ClearDefaults(result);

            return result;
        }
        public void SetValue(object value) {
            if (m_parent == null)
                throw new InvalidOperationException();
            SetValue(m_parent, value);
        }
        public void SetValue(object target, object value) {

            if (m_contract != null && !m_isTypedObject)
                value = m_contract.Deserialize(value);

            if (m_member != null) {
                m_member.SetValue(target, value);

            } else {
                var targetAsList = target as IList;

                if (targetAsList == null)
                    throw new InvalidOperationException();

                if (targetAsList.Count < m_index)
                    throw new InvalidOperationException();

                if (targetAsList.Count == m_index)
                    targetAsList.Add(value);

                else
                    targetAsList[m_index] = value;
            }
        }
        public VisitorNode ShallowCopy() {
            return ShallowCopy(o => o.ShallowCopyValue());
        }
        public VisitorNode ShallowCopy(Func<VisitorNode, object> copyValue) {
            VisitorNode copy = this;
            copy.m_value = copyValue(this);
            copy.m_parent = null;
            copy.m_index = m_index;
            copy.m_contract = m_contract;
            copy.m_member = m_member;
            return copy;
        }
        public object ShallowCopyValue() {

            // null
            if (!HasValue)
                return null;

            if (m_isTypedObject)
                return TypedObject.Make(m_deserializedValue, m_contract);

            object result = null;

            // simple
            if (ValueIsSimpleContract)
                result = Value;

            // xml
            else if (ValueIsXml)
                result = new XElement(ValueAsXElement);

            // enumeration
            else if (ValueIsEnumeration)
                result = (IList)Activate();

            // complex or group
            else if (ValueIsComplexContract || ValueIsGroupContract)
                result = Activate();

            // unreachable
            else 
                throw new InvalidOperationException();

            return result;
        }
        #endregion

        #region Object Overrides
        public override string ToString() {
            return "Node: {0} {{ {1} }}"
                .Substitute(
                    m_member != null ?
                        m_member :
                        m_contract ?? (object)"null",
                    m_value ?? (object)"null");
        }
        public override bool Equals(object obj) {
            if (!(obj is VisitorNode))
                return false;

            var rhs = (VisitorNode)obj;

            var metadata = m_contract == rhs.m_contract && m_member == rhs.m_member;
            if (metadata == false)
                return false;

            if (m_value == null)
                return rhs.m_value == null;

            if (m_value is XElement) {
                var xmlComparer = new XNodeEqualityComparer();
                var result = xmlComparer.Equals((XElement)m_value, rhs.m_value as XElement);
                return result;
            }

            return m_value.Equals(rhs.m_value);
        }
        public override int GetHashCode() {
            return base.GetHashCode();
        }
        #endregion
    }
}